package com.cskaoyan.javase.oop2._1extends._10override._0introduction;

/**
 * 实际开发中,绝大多数情况下,成员变量都是private修饰的
 * 是无法使用对象名点访问的
 * 这时,可能需要依赖getter/setter方法完成属性的访问
 * 所以方法调用在代码中很常见,方法的覆盖要比属性的隐藏更重要
 *
 * 方法的覆盖/重写
 * 在上一节一样，父子类中的成员方法也是可以同名的。
 * 那么我们直接创建子类对象，使用子类引用接收。
 * 调用同名方法，会发现访问的结果是子类中方法的行为
 * 那么父类中的那个同名成员方法去哪了呢？还能够访问吗？
 *
 * 首先它肯定是存在的，方法不过是方法区的二进制指令合集罢了
 * 类加载时期，装载进方法区，方法的指令合集不会物理消失。
 *
 * 之所以该小节称之为"方法的覆盖"是因为使用"对象名点"调用成员方法
 * 是访问不到该方法的,结果仍然是子类的行为
 *
 * 子类引用指向子类对象,只要是"对象名点"调用同名test方法,结果一定是子类的行为
 * 这就是方法的覆盖
 *
 * 接下来研究"对象名点成员方法"的调用机制:
 *      1.访问范围，一个引用究竟能够调用到哪些成员方法？根据什么来决定？
 *      2.调用结果，如果是父子类同名成员方法，那么结果是什么？根据什么来决定？
 *
 * 在具体研究之前,我们还需要知道,在存在继承关系时,new创建对象的方式:
 *      1.父类引用指向父类对象
 *      2.子类引用指向子类对象
 *      3.父类引用指向子类对象(向上转型)
 *
 * 首先,先研究访问范围,这里和"对象名点成员变量"没有区别,仍然是看引用的数据类型即可.
 *      如果引用是子类类型, 访问范围是父类 + 子类
 *      如果引用是父类类型, 访问范围是父类,没有子类
 *
 * 对象名点成员方法它调用的结果是什么呢?
 *      1.父类引用指向父类对象
 *      2.子类引用指向子类对象
 *          上面两个,引用和对象的类型一致,分别体现父类行为和子类行为
 *      3.父类引用指向子类对象(向上转型)
 *          结果是子类行为
 *
 *      综上,对象名点成员方法调用的结果要根据具体的对象类型来决定.(非常非常重用)
 *          如果对象的类型中,存在该方法,那么就体现对象的类型中的行为(重点)
 *          如果对象的类型中,不存在该方法,这个方法就是继承自父类,那么就体现父类的行为
 *
 * 总结:
 *  对象名点访问成员方法的范围和结果，发现：
 *      1.访问范围仍然是要靠 引用的数据类型 来决定。
 *      2.但是方法的调用结果，就需要考虑对象的具体类型，是何种对象，就体现出它的行为。
 *
 *
 * @since 16:28
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        /*Son s = new Son();
        s.test();
        s.invokeSuperTest();
        s.invokeTestFather();*/

        // 1.父类引用指向父类对象
        Father f = new Father();
        f.test();
        // f.testFather();

        // 2.子类引用指向子类对象
        Son s = new Son();
        s.test();
        // s.testSon();
        // s.testFather();

        // 3.父类引用指向子类对象(向上转型)
        Father fs = new Son();
        // fs.testFather();
        fs.test();
        // 强转后可以调用子类独有方法,不强转不行
        // ((Son) fs).testSon();
        // fs.testSon();
    }
}

class Father {
    public void test() {
        System.out.println("Father");
    }
    public void testFather(){
        System.out.println("Father only");
    }
    public void invokeTestFather(){
        test();
    }
}

class Son extends Father {
    public void test() {
        System.out.println("Son");
    }
    public void testSon(){
        System.out.println("Son only");
    }
    /*public void invokeSuperTest(){
        super.test();
    }*/
}